package edu.seu.mart.dao;

import edu.seu.mart.entity.SystemRequest;
import edu.seu.mart.entity.SystemRequestHolder;
import edu.seu.mart.entity.Pager;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;

import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.math.BigInteger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by samsung on 2015/3/8.
 */
public class BaseDao<T> implements IBaseDao<T> {

    @Resource
    private SessionFactory sessionFactory;
    private Class<?> clazz;

    protected SystemRequest getSystemRequest() {
        SystemRequest sr = SystemRequestHolder.getSystemRequest();
        if (sr == null) {
            sr = new SystemRequest();
        }
        return sr;
    }

    /**
     * 使用反射技术得到T的真实类型
     */
    public Class<?> getClazz() {
        if (clazz == null) {
            ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
            clazz = (Class<?>) pt.getActualTypeArguments()[0];
        }
        return clazz;
    }

    /**
     * 获取和当前线程绑定的Session
     */
    protected Session getSession() {
        return sessionFactory.getCurrentSession();
    }

    @Override
    public void add(T t) {
        this.getSession().save(t);
    }

    public void addEntity(Object entity) {
        this.getSession().save(entity);
    }

    @Override
    public void update(T t) {
        this.getSession().update(t);
    }

    public void updateEntity(Object entity) {
        this.getSession().update(entity);
    }

    @Override
    public void delete(int id) {
        this.getSession().delete(this.load(id));
    }

    public void deleteEntity(Object entity) {
        this.getSession().delete(entity);
    }

    public void saveOrUpdateEntity(Object entity) {
        this.getSession().saveOrUpdate(entity);
    }

    @Override
    public T load(int id) {
        return (T) this.getSession().load(this.getClazz(), id);
    }

    public Object loadEntity(Class clazz, int id) {
        return this.getSession().load(clazz, id);
    }

    /**
     * 设置别名参数
     */
    private void setAliasParameter(Query query, Map<String, Object> alias) {
        if (alias != null) {
            Set<String> keys = alias.keySet();
            for (String key : keys) {
                Object val = alias.get(key);
                if (val instanceof Collection) {
                    query.setParameterList(key, (Collection) val);
                } else {
                       query.setParameter(key, val);
                }
            }
        }
    }

    /**
     * 设置参数
     */
    private void setParameter(Query query, Object[] args) {
        if (args != null && args.length > 0) {
            int index = 0;
            for (Object arg : args) {
                query.setParameter(index++, arg);
            }
        }
    }

    /**
     * 初始化排序规则
     */
    private String initSort(String hql) {
        String order = this.getSystemRequest().getOrder();
        String sort = this.getSystemRequest().getSort();
        if (sort != null && !"".equals(sort.trim())) {
            hql += " order by " + sort;
            if (!"desc".equals(order)) {
                hql += " asc";
            } else {
                hql += " desc";
            }
        }
        return hql;
    }

    @Override
    public List<T> list(String hql, Map<String, Object> alias, Object ...args) {
        hql = this.initSort(hql);
        Query query = this.getSession().createQuery(hql);
        this.setAliasParameter(query, alias);
        this.setParameter(query, args);
        return query.list();
    }

    @Override
    public List<T> list(String hql, Object ...args) {
        return this.list(hql, null, args);
    }

    public List<?> listObj(String hql, Map<String, Object> alias, Object ...args) {
        hql = this.initSort(hql);
        Query query = this.getSession().createQuery(hql);
        this.setAliasParameter(query, alias);
        this.setParameter(query, args);
        return query.list();
    }

    public List<?> listObj(String hql, Object ...args) {
        return this.listObj(hql, null, args);
    }

    /**
     * 设置分页参数
     * @param query
     * @param pages
     */
    private void setPagers(Query query, Pager pages) {
        Integer pageSize = this.getSystemRequest().getPageSize();
        Integer pageOffset = this.getSystemRequest().getPageOffset();
        if (pageOffset < 0) pageOffset = 0;
        if (pageSize < 0) pageSize = 15;
        pages.setOffset(pageOffset);
        pages.setSize(pageSize);
        query.setFirstResult(pageOffset).setMaxResults(pageSize);
    }

    /**
     * 查询记录数
     */
    private String getCountHql(String hql, boolean isHql) {
        String e = hql.substring(hql.indexOf("from"));
        String c = "select count(*) " + e;
        if (isHql) {
            c = c.replaceAll("fetch", "");
        }
        return c;
    }

    @Override
    public Pager<T> find(String hql, Map<String, Object> alias, Object ...args) {
        hql = this.initSort(hql);
        String cq = this.getCountHql(hql, true);
        Query cquery = this.getSession().createQuery(cq);
        Query query = this.getSession().createQuery(hql);
        // 设置别名参数
        this.setAliasParameter(query, alias);
        this.setAliasParameter(cquery, alias);
        // 设置参数
        this.setParameter(query, args);
        this.setParameter(cquery, args);
        Pager<T> pages = new Pager<T>();
        this.setPagers(query, pages);
        List<T> datas = query.list();
        pages.setDatas(datas);
        Long total = (Long) cquery.uniqueResult();
        pages.setTotal(total);
        return pages;
    }

    @Override
    public Pager<T> find(String hql, Object ...args) {
        return this.find(hql, null, args);
    }

    public Pager<T> findNoCount(String hql, Map<String, Object> alias, Object ...args) {
        hql = this.initSort(hql);
        Query query = this.getSession().createQuery(hql);
        this.setAliasParameter(query, alias);
        this.setParameter(query, args);
        Pager<T> pages = new Pager<T>();
        this.setPagers(query, pages);
        List<T> datas = query.list();
        pages.setDatas(datas);
        return pages;
    }

    public Pager<T> findNoCount(String hql, Object ...args) {
        return this.findNoCount(hql, null, args);
    }

    public void updateByHql(String hql, Object ...args) {
        Query query = this.getSession().createQuery(hql);
        this.setParameter(query, args);
        query.executeUpdate();
    }

    public Object queryObject(String hql, Map<String, Object> alias, Object ...args) {
        Query query = this.getSession().createQuery(hql);
        this.setAliasParameter(query, alias);
        this.setParameter(query, args);
        return query.uniqueResult();
    }

    public Object queryObject(String hql, Object ...args) {
        return this.queryObject(hql, null, args);
    }

    @Override
    public List<T> listBySql (String sql, Map<String, Object> alias, Class<?> clazz, boolean hasEntity, Object ...args) {
        sql = this.initSort(sql);
        SQLQuery sq = this.getSession().createSQLQuery(sql);
        this.setAliasParameter(sq, alias);
        this.setParameter(sq, args);
        if (hasEntity) {
            sq.addEntity(clazz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clazz));
        }
        return sq.list();
    }

    @Override
    public List<T> listBySql (String sql, Class<?> clazz, boolean hasEntity, Object ...args) {
        return this.listBySql(sql, null, clazz, hasEntity, args);
    }

    @Override
    public Pager<T> findBySql (String sql, Map<String, Object> alias, Class<?> clazz, boolean hasEntity, Object ...args) {
        sql = this.initSort(sql);
        String cquery = getCountHql(sql, false);
        SQLQuery sq = this.getSession().createSQLQuery(sql);
        SQLQuery cq = this.getSession().createSQLQuery(cquery);
        this.setAliasParameter(sq, alias);
        this.setAliasParameter(cq, alias);
        this.setParameter(sq, args);
        this.setParameter(cq, args);
        Pager<T> pages = new Pager<T>();
        this.setPagers(sq, pages);
        if (hasEntity) {
            sq.addEntity(clazz);
        } else {
            sq.setResultTransformer(Transformers.aliasToBean(clazz));
        }
        List<T> datas = sq.list();
        pages.setDatas(datas);
        long total = ((BigInteger) cq.uniqueResult()).longValue();
        pages.setTotal(total);
        return pages;
    }

    @Override
    public Pager<T> findBySql (String sql, Class<?> clazz, boolean hasEntity, Object ...args) {
        return this.findBySql(sql, null, clazz, hasEntity, args);
    }

    public List<T> listByAlias(String hql, Map<String, Object> alias) {
        return this.list(hql, null, alias);
    }

    public Pager<T> findByAlias(String hql, Map<String, Object> alias) {
        return this.find(hql, null, alias);
    }

    public List<T> listByAliasSql(String sql, Map<String, Object> alias, Class<?> clazz, boolean hasEntity) {
        return this.listBySql(sql, alias, clazz, hasEntity);
    }

    public Pager<T> findByAliasSql(String sql, Map<String, Object> alias, Class<?> clazz, boolean hasEntity) {
        return this.findBySql(sql, alias, clazz, hasEntity);
    }

    public Object queryObjectByAlias(String hql, Map<String, Object> alias) {
        return this.queryObject(hql, null, alias);
    }

//    public int getMaxOrder(Integer pid, String clazz) {
//        String hql = "select max(o.orderNum) from " + clazz + " o where o.parent.id = " + pid;
//        if(pid == null || pid == 0) hql = "select max(o.orderNum) from " + clazz + " o where o.parent is null";
//        Object obj = this.queryObject(hql);
//        return obj == null ? 0 : (Integer) obj;
//    }
//
//    public int getMaxOrder(String clazz) {
//        String hql = "select max(o.orderNum) from " + clazz + " o ";
//        Object obj = this.queryObject(hql);
//        return obj == null ? 0 : (Integer) obj;
//    }
//
//    public void updateSort(Integer[] ids, String clazz) {
//        int index = 1;
//        String hql = "update " + clazz + " m set m.orderNum = ? where m.id = ?";
//        for(Integer id : ids) {
//            this.updateByHql(hql, new Object[]{index++,id});
//        }
//    }
//
//    public Object loadBySn(String sn, String clazz) {
//        String hql = "select c from " + clazz + " c where c.sn = ?";
//        return this.queryObject(hql, sn);
//    }
}
